//==============================================================================
//
//         DCube - the 3d - array collection in the ofc-library
//
//               Copyright (C) 2003  Dick van Oudheusden
//  
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//==============================================================================
// 
//  $Date: 2005-01-31 06:44:29 $ $Revision: 1.3 $
//
//==============================================================================

#include "ofc/DCube.h"

#if _INTERFACE_

#include "ofc/config.h"


// The cube collection stores objects in a 3d array that can grow. 
// Objects can be accessed by an index. Due to the easy access there 
// is no iterator.

@interface DCube : Object
{
@private
  id            *_objects;  // the stored objects
  int            _columns;  // the number of columns
  int            _rows;     // the number of rows
  int            _layers;   // the number of layers
  long           _length;   // the length of the cube
}

#endif



@implementation DCube

//// Constructors

//
// Initialise an empty cube
//
// @return the object
//

- (DCube *) init
{
  [self init :1 :1 :1];

  return self;
}

//
// Initialise a cube with columns, rows and layers
//
// @param columns  the number of columns
// @param rows     the number of rows
// @param layers   the number of layers
//
// @return the object
//

- (DCube *) init :(int) columns :(int) rows :(int) layers
{
  long i;
   
  [super init];

  if (columns < 1)
  {
    WARNING(DW_INVALID_ARG, "columns");
    columns = 1;
  }
  if (rows < 1)
  {
    WARNING(DW_INVALID_ARG, "rows");
    rows = 1;
  }
  if (layers < 1)
  {
    WARNING(DW_INVALID_ARG, "layers");
    layers = 1;
  }
  
  _columns = columns;
  _rows    = rows;
  _layers  = layers;
  _length  = _columns * _rows * _layers;
   
  _objects = (id *) objc_malloc(sizeof(id) * _length);
  
    
  for (i = 0; i < _length; i++)
    _objects[i] = nil;

  return self;
}


//// Copy related methods

//
// Deepen a copy of the cube
//
// @return the object
//

- deepen
{
  long  i;
  id   *origin = _objects;
  
  [super deepen];
  
  _objects = (id *) objc_malloc(sizeof(id) * _length);
  
    
  for (i = 0; i < _length; i++)
  {
    _objects[i] = [origin[i] copy];
  }
  
  return self;
}


//// Destructor

//
// Free the cube, not the stored objects
//
// @return the object
//

- shallowFree
{
  long i;

  for (i = 0; i < _length; i++)
  {
    _objects[i] = nil;
  }
  
  objc_free(_objects);
  
  [super free];

  return self;
}

//
// (Deep) free the cube (incl. the stored objects)
//
// @return the object
//

- free
{
  long i;

  for (i = 0; i <_length; i++)
  {
    if (_objects[i] != nil)
    {
      [_objects[i] free];
    }
  }

  [self shallowFree];

  return self;
}


//// Member methods

//
// Return the length of the cube (columns * rows * layers)
//
// @return the length
//

- (long) length
{
  return _length;
}


//
// Return the number of columns
// 
// @return the number of columns
// 

- (int) columns
{
  return _columns;
}

//
// Set the number of columns (only growth)
// 
// @param columns  the number of columns
// 
// @return the object
// 

- (DCube *) columns :(int) columns
{
  if (columns > _columns)
  {
    long  length = _rows * columns * _layers;
    long  i;
    long  row,col,layer;

    id   *origin = _objects;
    
    _objects = (id *) objc_malloc(sizeof(id) * length);
     
    for (i = 0; i < length; i++)
    {
      _objects[i] = nil;
    }
    
    for (col = 0; col < _columns; col++)
      for (row = 0; row < _rows; row++)
        for (layer = 0; layer < _layers; layer++)
        {
          _objects[col + row * columns + layer * (columns * _rows)] = 
             origin[col + row * _columns + layer * (_columns * _rows)];
        }
    
    objc_free(origin);
    
    _columns = columns;
    _length  = length;
  }
  
  return self;
}

//
// Return the number of rows
// 
// @return the number of rows
// 

- (int) rows
{
  return _rows;
}

//
// Set the number of rows (only growth)
// 
// @param rows     the number of rows
// 
// @return the object
// 

- (DCube *) rows :(int) rows
{
  if (rows > _rows)
  {
    long  length = rows * _columns * _layers;
    long  i;
    long  row,col,layer;

    id   *origin = _objects;
    
    _objects = (id *) objc_malloc(sizeof(id) * length);
     
    for (i = 0; i < length; i++)
    {
      _objects[i] = nil;
    }
    
    for (col = 0; col < _columns; col++)
      for (row = 0; row < _rows; row++)
        for (layer = 0; layer < _layers; layer++)
        {
          _objects[col + row * _columns + layer * (_columns * row)] = 
             origin[col + row * _columns + layer * (_columns * _rows)];
        }
    
    objc_free(origin);
    
    _rows   = rows;
    _length = length;
  }
   
  return self;
}

//
// Return the number of layers
// 
// @return the number of layers
// 

- (int) layers
{
  return _layers;
}

//
// Set the number of layers (only growth)
// 
// @param layers   the number of layers
// 
// @return the object
// 

- (DCube *) layers :(int) layers
{
  if (layers > _layers)
  {
    long  length = _rows * _columns * layers;
    long  i;
    long  row,col,layer;
    
    id   *origin = _objects;
    
    _objects = (id *) objc_malloc(sizeof(id) * length);
     
    for (i = 0; i < length; i++)
    {
      _objects[i] = nil;
    }
    
    for (col = 0; col < _columns; col++)
      for (row = 0; row < _rows; row++)
        for (layer = 0; layer < _layers; layer++)
        {
          _objects[col + row * _columns + layer * (_columns * _rows)] = 
             origin[col + row * _columns + layer * (_columns * _rows)];
        }
    
    objc_free(origin);
    
    _layers = layers;
    _length = length;
  }
   
  return self;
}

//
// Check if an index is valid
//
// @param col      the column (0..)
// @param row      the row (0..)
// @param layer    the layer (0..)
//
// @return is it valid ?
//

- (BOOL) isValid :(int) col :(int) row :(int) layer
{
  return ((col   >= 0) && (col   < _columns) && 
          (row   >= 0) && (row   < _rows)    &&
          (layer >= 0) && (layer < _layers));
}


//// Main methods

//
// Set an object in the cube
//
// @param col      the column (0..)
// @param row      the row (0..)
// @param layer    the layer (0..)
// @param obj      the object to be set
//
// @return the previous stored object in the cube (or nil)
//

- (id) set :(int) col :(int) row :(int) layer :(id) obj
{
  id prev = nil;
  
  if ([self isValid :col :row :layer])
  {
    long index = col + row * _columns + layer * (_columns * _rows);
     
    prev = _objects[index];

    _objects[index] = obj; 
  }
  else
  {
    WARNING(DW_ARG_OUT_RANGE, "col/row/layer");
  }
  
  return prev;
}

//
// Get an object from the cube
//
// @param col      the column (0..)
// @param row      the row (0..)
// @param layer    the layer (0..)
//
// @return the object in the cube (or nil)
//

- (id) get :(int) col :(int) row :(int) layer
{
  id obj = nil;

  if ([self isValid :col :row :layer])
  {
    obj = _objects[col + row * _columns + layer * (_columns * _rows)];
  }
  else
  {
    WARNING(DW_ARG_OUT_RANGE, "col/row/layer");
  }
  
  return obj;
}

//
// Count the number of occurences of an object
//
// @param obj      the object to be counted
//
// @return the number of occurences
//

- (long) count :(id) obj
{
  long i;
  long c = 0L;

  for (i = 0; i <_length; i++)
  {
    if (_objects[i] == obj)
      c++;
  }

  return c;
}

//
// Check if an object is in the cube
//
// @param obj      the object
//
// @return is it in the cube ?
//

- (BOOL) has :(id) obj
{
  long i = 0L;
  
  while ((i < _length) && (_objects[i] != obj))
    i++;
   
  return (i < _length);
}

//
// Perform a selector on each object in the cube
//
// @param sel      the selector
//
// @return the object
//

- (DCube *) each :(SEL) sel
{
  long i;

  for (i = 0L; i < _length; i++)
  {
    if (_objects[i] != nil)
    {
      [_objects[i] perform :sel];
    }
  }

  return self;
}

@end

/*==========================================================================*/
